package com.lcykcl.ttgen;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public class Manager {
	// HashMap for generating graphs.
	// FL stands for fall lecture; FT stands for fall tutorial
	private HashMap<String, ArrayList<Node>> FLNodes;
	private HashMap<String, ArrayList<Node>> SLNodes;

	private HashMap<String, ArrayList<Node>> FTNodes;
	private HashMap<String, ArrayList<Node>> STNodes;

	// Lists of the combinations of year sections
	// L stands for lectures and T stands for tutorials
	// Previous form: private ArrayList<Node> lectureStartNodes;
	private ArrayList<ArrayList<Node>> LYearSections;
	private ArrayList<ArrayList<Node>> TYearSections;

	// Completed timetables; FL stands for lectures for the fall semester
	// This collection contains nodes for the completed
	// time table of Lectures/Tutorials for a year

	private ArrayList<Node> FLTimeTable;
	private ArrayList<Node> FTTimeTable;

	private ArrayList<Node> SLTimeTable;
	private ArrayList<Node> STTimeTable;

	private Persona p;

	TimeTable FLttb;
	TimeTable SLttb;
	TimeTable FTttb;
	TimeTable STttb;
	


	public Manager(ArrayList<ArrayList<Node>> FList, ArrayList<ArrayList<Node>> SList, Persona p) {
		// initiation
		FLNodes = new HashMap<String, ArrayList<Node>>();
		SLNodes = new HashMap<String, ArrayList<Node>>();

		FTNodes = new HashMap<String, ArrayList<Node>>();
		STNodes = new HashMap<String, ArrayList<Node>>();

		// update the FLNodes,SLNodes,FTNodes and STNodes
		//ParseDataBase(FSYList); This became the method called getLTnodes in DataRetriever 
		//We got LTnode from DataRetrever for fList and SList
		//Example of FList:
		//FList[0] => Lecture
		//FList[1] => Tutorial
		
		UpdateNodeDict(FLNodes,FList.get(0));
		UpdateNodeDict(FTNodes,FList.get(1));
		UpdateNodeDict(SLNodes,SList.get(0));
		UpdateNodeDict(STNodes,SList.get(1));
		
		// Update L;T yearSections
		LYearSections = getYearSections(FLNodes);
		TYearSections = getYearSections(FTNodes);

		// Set persona
		this.p = p;

		//Initiate TimeTable objects 
		// FLttb = new TimeTable('L', FLNodes, null, p);
		FLttb = new TimeTable(LYearSections, FLNodes, null, p);
		SLttb = new TimeTable(LYearSections, SLNodes, null, p);
		FTttb = new TimeTable(TYearSections, FTNodes, null, p);
		STttb = new TimeTable(TYearSections, STNodes, null, p);
		
		
	}
	//UpdateNodeDict(LNodeDict, TNodeDict, nodeList);
	//nodeList: A list containing nodes for one specific semester
	//nodesDict: A dict to be updated for the lecture
        /**
         * get the list of Nodes as input from the constructor and update the dict for that
         * particular sibling node list (e.g.FLNodes, FTNodes, etc.).
         * @param nodesDict
         * @param nodeList 
         */
	private void UpdateNodeDict(HashMap<String, ArrayList<Node>> nodesDict, ArrayList<Node> nodeList) {
		//parse the nodeList
		//An example of info in Node class: {code, session, sec_type, sec_num, time, loc, inst}
		//Instance variables in Node: edge, cost, info;
		//Key: code, Value: node
		
		
		int size = nodeList.size();
		
		for (int i = 0; i < size; i++) {
			Node courseNode = nodeList.get(i);
			String courseName = courseNode.getCode();
			if (nodesDict.containsKey(courseName)) {
				nodesDict.get(courseName).add(courseNode);
			}else{
				ArrayList<Node> courseNodeList = new ArrayList<Node> ();
				courseNodeList.add(courseNode);
				nodesDict.put(courseName, courseNodeList);
				}
			}
		}

	public void updateAllPersonas(Persona p){
		//update Persona for all timetable objects
		FLttb.updatePersona(p);
		SLttb.updatePersona(p);
		FTttb.updatePersona(p);
		STttb.updatePersona(p);
	}

	public void generateCompleteTimeTable() {
		//generate a complete timetable
		
		//glue timetable for F sections and S sections for lectures
		//result: either FLttb and SLttb has a complete timetables for the lecture
		//or at least one of them has conflict timetable but it can't be resolved
		//because there is no element in the yearSections left 
		glueTimeTables(FLttb, SLttb);
		
		//after making a timetable for both sections for the lectures
		//it is necessary to update fixed nodes for the timetable
		//objects for tutorials according to the timetable generated 
		//for the lecture
		FTttb.updateFixed(FLttb.getTimeTable());
		STttb.updateFixed(SLttb.getTimeTable());
		
		//if tutorial timetable for the both sections can't be generated
		//and if there is some elements left in the yearSections in 
		//the timetable object for lecture(i.e.,FLttb.isPossibletoProceed() )
		if (glueTimeTables(FTttb, STttb) == false
				&& FLttb.isPossibletoProceed()) {
			//update yearSections in the timetable objects for lectures
			//(i.e., we should try different combination of year sections)
			FLttb.updateYearSections();
			SLttb.updateYearSections();
			generateCompleteTimeTable();
		}
		//return best possible timetables
		FLTimeTable = FLttb.getTimeTable();
		SLTimeTable = SLttb.getTimeTable();
		FTTimeTable = FTttb.getTimeTable();
		STTimeTable = STttb.getTimeTable();
	}

        
        /**
         * Returns true if t1 and t2 are glued together for two semesters. Glue together means
         * use generateTTB to generate allTimetables for both semesters.
         * @param t1
         * @param t2
         * @return 
         */
	private boolean glueTimeTables(TimeTable t1, TimeTable t2) {
		//glue FL and SL or FT and ST together
		//FL stands for the timetable for the lectures in fall semester
		//FT stands for the timetable for the tutorials in fall semester
		
		//generate a timetable for t1
		t1.generateTTB();
		//if t1 got conflict and still has combinations for in yearSections 
		//try again to generate another timetable for t1 and update yearSection
		//for t2
		while (!t1.isWorkComplete()) {
			t1.tryAgain();
			t2.updateYearSections();
		}
		//generate a timetable for t2
		t2.generateTTB();
		//if t2 got conflict and t1 still has combinations in yearSections 
		//then update yearSections for both t1 and t2 and try to glue them again
		if (t2.isConflict() && t1.isPossibletoProceed()) {
			t1.updateYearSections();
			t2.updateYearSections();
			glueTimeTables(t1, t2);
			//if t1 or t2 got conflict and t1 doesn't have any combinations
			//in yearSections left
			//then gluing two timetable is failed and return false
		} else if (t2.isConflict() || t1.isConflict()) {
			return false;
		}
		//if t1 is not conflict and t2 is not conflict then return true
		return true;
	}

	
	//Desired format of the output:
	//String[][] courseList = { { "csc236","BA1201", "M3", "T1" },
		//	{ "csc108","BA1201", "T2", "W9-11" },
			//{ "csc343","BA1201", "M3-5", "R1-2" }};
	
	//I need to change the type of input as  ArrayList<ArrayList<String>> from String[][]
//	public ArrayList<ArrayList<String>> getFallTimeTable() {
//		// put tutoTimeTable and lectureTimetable in the desired format.
//		ArrayList<ArrayList<String>> timetableToReturn =  getTimeTable(this.FLTimeTable);
//		timetableToReturn.addAll(getTimeTable(this.FTTimeTable));
//		return timetableToReturn;
//	}
//	
//	public ArrayList<ArrayList<String>> getSpringTimeTable() {
//		// put tutoTimeTable and lectureTimetable in the desired format.
//		ArrayList<ArrayList<String>> timetableToReturn =  getTimeTable(this.SLTimeTable);
//		timetableToReturn.addAll(getTimeTable(this.STTimeTable));
//		return timetableToReturn;
//	}
//	
//	private ArrayList<ArrayList<String>> getTimeTable(ArrayList<Node> selected) {
//		ArrayList<ArrayList<String>> courseList = new ArrayList<ArrayList<String>>();
//		for (int i = 0; i < selected.size(); i++) {
//			courseList.add(toStringArray(selected.get(i)));
//		}
//		return courseList;
//	}

//	public ArrayList<String> toStringArray(Node nd) {
//		ArrayList<String> ndInfo = nd.getInfo();
//		// An example of info: {code, session, sec_type, sec_num, time, loc, inst}
//		ArrayList<String> infoToreturn = new ArrayList<String>();
//		infoToreturn.add(ndInfo.get(0));
//		infoToreturn.add(ndInfo.get(5));
//		String[] timeList = nd.getTimeList();
//		for (int i = 0; i < timeList.length; i++) {
//			infoToreturn.add(timeList[i]);
//		}
//		return infoToreturn;
//	}
	
	//So main activity calls return fall sibling map to get the courseMap
	public HashMap<String, ArrayList<Node>> returnFallSiblingMap() {
		HashMap<String, ArrayList<Node>> mapToReturn = (HashMap<String, ArrayList<Node>>) this.FLNodes.clone();
		mapToReturn.putAll(FTNodes);
		return mapToReturn;
	}
	//and main activity calls return fall timetable to get the course list
	public ArrayList<Node> returnFallTimeTable() {
		ArrayList<Node> listToReturn = (ArrayList<Node>) this.FLTimeTable.clone();
		listToReturn.addAll(FTTimeTable);
		return listToReturn;
	}
	//before the fall timetable activity calls new activity for the spring session it should call this and send this information to the activity
	public HashMap<String, ArrayList<Node>> returnSpringSiblingMap() {
		HashMap<String, ArrayList<Node>> mapToReturn =  (HashMap<String, ArrayList<Node>>) this.SLNodes.clone();
		mapToReturn.putAll(STNodes);
		return mapToReturn;
	}
	//before the fall timetable activity calls new activity for the spring session it should call this and send this information to the activity
	public ArrayList<Node> returSpringTimeTable() {
		ArrayList<Node> listToReturn = (ArrayList<Node>) this.SLTimeTable.clone();
		listToReturn.addAll(STTimeTable);
		return listToReturn;
	}
        
        /**
         * returns a list of all combinations of Y section courses to be used for the timetable.
         * @param lectureNodes
         * @return 
         */
	private ArrayList<ArrayList<Node>> getYearSections(
			HashMap<String, ArrayList<Node>> lectureNodes) {
		// return a list of all combinations of year sections
		ArrayList<ArrayList<Node>> permutations = new ArrayList<ArrayList<Node>> ();
		
		
		Iterator<String> iter = lectureNodes.keySet().iterator();
		while (iter.hasNext()) {
			//get the key
			ArrayList<ArrayList<Node>> perCopyForAll = new ArrayList<ArrayList<Node>> ();
			String key = (String) iter.next();
			//check if the key is Y section 
			//if yes,
			 if (key.charAt(6)== 'Y') {
				ArrayList<Node> values = lectureNodes.get(key);
				for (int i = 0; i < values.size(); i++) {
					ArrayList<ArrayList<Node>> perCopyForOne = (ArrayList<ArrayList<Node>>) permutations.clone();
					for (int j = 0; j < perCopyForOne.size(); j++) {
						perCopyForOne.get(j).add(values.get(i));
					}
					perCopyForAll.addAll(perCopyForOne);
				}
				permutations = perCopyForAll;
			}
			 
			//then for all keys 
			//do permute
		}
		return permutations;
	}

}
